import requests
import numpy as np
from typing import ClassVar
from .config_load import app_config
from langchain_openai import ChatOpenAI
import dashscope
import math
# 使用以下命令安装Pillow库：pip install Pillow
from PIL import Image

class SILICONFLOWVL():
    """
        初始化多模态大模型
    """
    
    VL_MODEL: ClassVar[str]         = app_config['siliconflow']['vl_model'] 
    API_URL: ClassVar[str]          = app_config['siliconflow']['base_url']
    API_KEY: ClassVar[str]          = app_config['siliconflow']['api_key']
    def __init__(self) -> None:
        self.vl_obj = ChatOpenAI(
            openai_api_base=self.API_URL,
            openai_api_key=self.API_KEY,  
            model_name=self.VL_MODEL, 
        )

    def create_vl(self)-> object:
        """
            Qwen2-VL最高支持像素是3584 * 3584= 12845056，最低支持像素是56 * 56 = 3136，
            会对先对每张图片长短边均放缩至28的倍数 (h * 28) * (w * 28)。如果不在最小像素和最大像素区间内，
            再等比缩放至该区间。

            detail=low时将所有图片resize为448*448尺寸，最终对应256 token；
            detail=high时等比缩放，首先将长宽按照最近的28倍数向上取整，然后再等比缩放至像素区间 (3136, 12845056)，
            并保证长宽均为28整数倍。

            示例：
                1、224 * 448和1024 x 1024和3172 x 4096的图片，选择 detail=low 时，均消耗 256 tokens；
                2、224 * 448的图片，选择 detail=high 时，因为 224 * 448 在像素区间内，且长宽均为28倍数，
                3、消耗 (224/28) * (448/28) = 8 * 16 = 128 tokens；
                1024 * 1024的图片，选择detail=high时，将长宽按照28的倍数向上取整至 1036 * 1036，
                4、该数值在像素区间内，消耗 (1036/28) * (1036/28) = 1369 tokens；
                3172 * 4096的图片，选择detail=high 时，将长宽按照28的倍数向上取整至3192 * 4116，
                5、该值超过最大像素，再将长宽等比例缩小至3136 * 4060，消耗 (3136/28) * (4060/28) = 16240 tokens。
        """

        return self.vl_obj
    

class Qwen_VL():
    """
        初始化多模态大模型
    """
    VL_MODEL: ClassVar[str]         = app_config['aliyun']['vl_model'] 
    API_URL: ClassVar[str]          = app_config['aliyun']['base_url']
    API_KEY: ClassVar[str]          = app_config['aliyun']['api_key']

    def __init__(self) -> None:
        self.vl_obj = ChatOpenAI(
            openai_api_base=self.API_URL,
            openai_api_key=self.API_KEY,  
            model_name=self.VL_MODEL, 
        )

    def create_vl(self)-> object:

        return self.vl_obj




class aliyunVL():

    """
        初始化多模态大模型
    """
    VL_MODEL: ClassVar[str]         = app_config['aliyun']['vl_model']

    API_KEY: ClassVar[str]          = app_config['aliyun']['api_key']

    """
        每28x28像素对应一个Token，一张图最少需要4个Token。
    """
    def get_img_content_once(self, msg):

        """ 
            获取图像内容（单轮）
        """

        if not msg:

            raise ValueError("消息不能为空！")

        response = dashscope.MultiModalConversation.call(
            
            api_key=self.API_KEY,
            
            model=self.VL_MODEL, 
            
            messages=msg
        )

        return response
    
    
    def token_calculate(image_path):
        """ 
            计算图像的Token数量
        """
        # 打开指定的PNG图片文件
        image = Image.open(image_path)

        # 获取图片的原始尺寸
        height = image.height
        width = image.width
        
        # 将高度调整为28的整数倍
        h_bar = round(height / 28) * 28
        # 将宽度调整为28的整数倍
        w_bar = round(width / 28) * 28
        
        # 图像的Token下限：4个Token
        min_pixels = 28 * 28 * 4
        # 图像的Token上限：1280个Token
        max_pixels = 1280 * 28 * 28
            
        # 对图像进行缩放处理，调整像素的总数在范围[min_pixels,max_pixels]内
        if h_bar * w_bar > max_pixels:
            # 计算缩放因子beta，使得缩放后的图像总像素数不超过max_pixels
            beta = math.sqrt((height * width) / max_pixels)
            # 重新计算调整后的高度，确保为28的整数倍
            h_bar = math.floor(height / beta / 28) * 28
            # 重新计算调整后的宽度，确保为28的整数倍
            w_bar = math.floor(width / beta / 28) * 28
        elif h_bar * w_bar < min_pixels:
            # 计算缩放因子beta，使得缩放后的图像总像素数不低于min_pixels
            beta = math.sqrt(min_pixels / (height * width))
            # 重新计算调整后的高度，确保为28的整数倍
            h_bar = math.ceil(height * beta / 28) * 28
            # 重新计算调整后的宽度，确保为28的整数倍
            w_bar = math.ceil(width * beta / 28) * 28
        # return h_bar, w_bar

        print(f"缩放后的图像尺寸为：高度为{h_bar}，宽度为{w_bar}")
        token = int((h_bar * w_bar) / (28 * 28))

        print(f"图像的Token数为{token + 2}")
        return token+2


    

